package nl.Viewer.Sessions;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

import nl.Viewer.Application;
import nl.Viewer.SerializedObjects.Dataset;
import nl.Viewer.SerializedObjects.LookmarkInfo;
import nl.Viewer.SerializedObjects.Session;
import nl.Viewer.Sessions.Communication.Communication;
import nl.Viewer.Sessions.Communication.ConnectionStateListener;
import nl.Viewer.Sessions.Communication.SOAPServerException;
import nl.Viewer.Sessions.Communication.UserRequestException;
import nl.Viewer.Sessions.Communication.XMLParseException;

/**
 * Manager class for creating and/or closing sessions on the client side
 * 
 * @author Jeroen Verschuur
 * 
 */

public class SessionManager implements Runnable {
   private Communication                              communication;
   private volatile boolean                           shuttingDown             = false;
   private ConcurrentHashMap<Long, ClientSideSession> sessions                 = new ConcurrentHashMap<Long, ClientSideSession>();

   private ArrayList<ConnectionStateListener>         connectionStateListeners = new ArrayList<ConnectionStateListener>();

   private Thread                                     sessionStatusCheckThread = null;

   public SessionManager() {
   }

   public synchronized boolean setupServerConnection( String server, int port, String endpoint ) throws Exception {
      shuttingDown = false;
      sessions.clear();

      communication = new Communication( server + ":" + port, endpoint );
      // test the connection
      if( !pingServer() ) {
         throw getLastCommunicationException();
      }

      // start the getStatus-thread
      sessionStatusCheckThread = new Thread( this );
      sessionStatusCheckThread.start();

      return true;
   }

   public synchronized void shutdown() {
      shuttingDown = true;

      try {
         if( sessionStatusCheckThread != null )
            sessionStatusCheckThread.join( 2000 );
      }
      catch( InterruptedException ie ) {
         // ignore
      }
      finally {
         sessionStatusCheckThread = null;
      }

      // close all sessions
      for( ClientSideSession css : sessions.values() ) {
         try {
            css.shutdown();
            communication.closeSession( css.sessionId );
         }
         catch( Exception e ) {
            // ignore
         }
      }

      for( ConnectionStateListener csl : this.connectionStateListeners ) {
         csl.serverDisconnected( this );
      }
   }

   public int getSessionCount() {
      return sessions.size();
   }

   public boolean pingServer() {
      return communication.pingServer();
   }

   public boolean isConnectionVerified() {
      return communication.isVerified();
   }

   public Exception getLastCommunicationException() {
      return communication.getLastException();
   }

   public boolean login( String username, String password ) {
      boolean isVerified = communication.login( username, password );
      if( isVerified ) {
         synchronized( this ) {
            for( ConnectionStateListener csl : this.connectionStateListeners ) {
               csl.serverConnected( this, communication.getServerAddress() );
            }
         }

      }
      return isVerified;
   }

   public ArrayList<Dataset> getDatasets() throws Exception {
      return communication.getDatasets();
   }

   public ArrayList<LookmarkInfo> getLookmarks( Dataset selectedDataset ) throws Exception {
      return communication.getLookmarks( selectedDataset.datasetId );
   }

   public ClientSideSession createSession( int modalityId, int visualizationId ) throws UserRequestException,
         SOAPServerException {
      Long sessionId = communication.createSession( modalityId, visualizationId );

      ClientSideSession session = new ClientSideSession( sessionId, this );
      sessions.put( sessionId, session );

      Application.gui.updateGUI();

      return session;
   }

   public ClientSideSession createSessionFromLookmark( int lookmarkId ) throws UserRequestException,
         SOAPServerException {
      Long sessionId = communication.createSessionFromLookmark( lookmarkId );

      ClientSideSession session = new ClientSideSession( sessionId, this );
      sessions.put( sessionId, session );

      Application.gui.updateGUI();

      return session;
   }

   public void closeSession( ClientSideSession session ) {
      try {
         communication.closeSession( session.sessionId );
      }
      catch( Exception e ) {
         System.out.println( "closeSession call failed: " + e );
      }
      finally {
         session.shutdown();
         sessions.remove( session.sessionId );
         Application.gui.updateGUI();
      }
   }

   public Communication getCommunication() {
      return communication;
   }

   public void processStatusInfo( ArrayList<Session> alteredSessions ) {
      if( alteredSessions != null ) {
         for( Session s : alteredSessions ) {
            System.out.println( "Altered session: " + s );
            // find the client-side session and deliver the update when session is available
            ClientSideSession css = sessions.get( s.sessionId );

            if( css == null ) {
               // TODO: debug
               System.out.println( "No client-side session object found for status update on session " + s.sessionId );
            } else {
               css.updateSession( s );
            }
         }
      }
   }

   public void run() {
      // check for finished jobs
      while( !shuttingDown ) {
         if( communication.isVerified() ) {
            try {
               if( sessions.size() > 0 ) {
                  ArrayList<Session> alteredSessions = communication.getStatus();
                  processStatusInfo( alteredSessions );
               }
            }
            catch( UserRequestException u ) {
               u.printStackTrace();
               // TODO: what to do here?
               shuttingDown = true;
            }
            catch( XMLParseException e ) {
               e.printStackTrace();
            }
            catch( SOAPServerException s ) {
               System.out.println( "SESSIONMANAGER" );
               s.printStackTrace();
               // TODO: show dialog?
            }

            try {
               Thread.sleep( 100 );
            }
            catch( InterruptedException ie ) {
               // ignore
            }
         } else {
            // don't hang the cpu
            try {
               Thread.sleep( 1 );
            }
            catch( InterruptedException e ) {

            }
         }
      }
   }

   public synchronized void addConnectionStateListeners( ConnectionStateListener connectionStateListener ) {
      if( this.connectionStateListeners == null ) {
         this.connectionStateListeners = new ArrayList<ConnectionStateListener>();
      }
      this.connectionStateListeners.add( connectionStateListener );
   }
}
